%% Copyright (C) 2007 Vijay Gajula <gajulak@gmail.com>.
%% All rights reserved.
%%
%% Redistribution and use in source and binary forms, with or without
%% modification, are permitted provided that the following conditions
%% are met:
%%
%% 1. Redistributions of source code must retain the above copyright
%%    notice, this list of conditions and the following disclaimer.
%% 2. Redistributions in binary form must reproduce the above
%%    copyright notice, this list of conditions and the following
%%    disclaimer in the documentation and/or other materials provided
%%    with the distribution.
%%
%% THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
%% OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
%% WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
%% ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
%% DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
%% DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
%% GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
%% INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
%% WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
%% NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
%% SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

-module(xml_decode).
-vsn(1.0).
-author('gajulak@gmail.com').
-export([payload/2]).

-include("xmerl.hrl").

-define(NETCONF_MODULES, [
		  { services,		 mod_services	  },
		  { system,  	     mod_system       },
		  { user,            mod_user         },
		  { interface,       mod_interface    },
		  { routing, 	     mod_routing      },
		  { dhcpserver,		 mod_dhcpserver   },
		  { dnsserver,		 mod_dnsserver    },
		  { ipsec,           mod_ipsec        },
		  { certificate,     mod_certificates },
		  { vscan,           mod_vscan        }]).

payload(Sock, Data) ->
   case xmerl_scan:string(Data) of
      {error, _Reason} -> {error, _Reason};
      {P, _} ->
       	   case catch decode_element(Sock, P) of
	   	{'EXIT', _Reason} -> {error, _Reason};
		Result -> Result
	   end
   end.

decode_element(Sock, #xmlElement{name = rpc} = E) 
   when record(E, xmlElement) ->
	lists:foreach(fun(X) -> rpc_decode_element(Sock, X) end, 
			E#xmlElement.content); 
decode_element(_Sock, E) -> 
   	io:format("Element :~p~n",[E]),
	{error, {bad_element, E}}.
	
rpc_decode_element(Sock, #xmlElement{name = 'get-config'} = E) 
   when record(E, xmlElement) ->
	match_element(Sock, E#xmlElement.content);

%% rpc_decode_element(#xmlElement{name = Name} = E) 
   %% when record(E, xmlElement) ->
   	%% io:format("Name :~p~n",[Name]);
	%% decode_element(A#xmlElement.content);

rpc_decode_element(Sock, #xmlElement{name = 'edit-config'} = E) 
   when record(E, xmlElement) ->
   	%% io:format("edit-config:~p~n",[E]),
	edit_match_element(Sock, E#xmlElement.content);

rpc_decode_element(_Sock, _E) -> ok.

match_element(Sock, [#xmlElement{name = source}=E|T]) 
   when record(E, xmlElement) ->
	%% io:format("SOURCE:~p~n",[E]),
	match_element_source(Sock, E#xmlElement.content),
	match_element(Sock, T);

match_element(Sock, [#xmlElement{name = config}=E|T]) 
   when record(E, xmlElement) ->
	%% io:format("CONFIG:~p~n",[E]),
	match_element_config(Sock, E#xmlElement.content, []),
	match_element(Sock, T);

match_element(Sock, [_H|T]) -> match_element(Sock, T);
match_element(_Sock, []) -> ok.

match_element_source(Sock, [#xmlElement{name = _Name}=E|_T]) 
   when record(E, xmlElement) ->
	%% io:format("SOURCE Name :~p~n",[Name]),
	match_element_source(Sock, E#xmlElement.content);

match_element_source(Sock, [_H|T]) -> match_element_source(Sock, T);
match_element_source(_Sock, []) -> ok.


match_element_config(Sock, [#xmlElement{name = Name}=E|T], Reply) 
   when record(E, xmlElement) ->
	%% io:format("CONFIG Name : ~p Reply:~s~n",[Name, Reply]),
	Rep = process_config(Sock, Name, E#xmlElement.content),
	match_element_config(Sock, T, Reply ++ [Rep]);

match_element_config(Sock, [_H|T], Reply) -> match_element_config(Sock, T, Reply);
match_element_config(Sock, [], []) -> 
	  	     send_error(Sock, error);
match_element_config(Sock, [], Reply) -> 
	  	     send_reply(Sock, Reply).

find_module(_Type, []) -> [];
find_module(Type, [{Type, Module}|_T]) -> Module;
find_module(Type, [_H|T]) -> find_module(Type, T).

process_config(Sock, Type, E) ->
	case find_module(Type, ?NETCONF_MODULES) of
	    [] -> io:format("Module Not found :~p~n",[Type]),
	    	  ok;
	    Module ->
	        io:format("Module found :~p~n",[Module]),
		case erlang:apply(Module, process_get, [E]) of
	  	     {ok, Reply} ->  Reply;
	  	     _ -> []
	  	     %% {ok, Reply} -> send_reply(Sock, Reply);
	  	     %% _ -> send_error(Sock, error)
		end
	end.

process_target(Sock, Type, E) ->
	case find_module(Type, ?NETCONF_MODULES) of
	    [] -> io:format("Module Not found :~p~n",[Type]),
	    	  ok;
	    Module ->
	        io:format("Module found :~p~n",[Module]),
		case erlang:apply(Module, process_edit, [E]) of
	  	     {ok, Reply} -> send_reply(Sock, Reply);
	  	     _ -> send_error(Sock, error)
		end
	end.

send_reply(Sock, Data) ->
	%% io:format("In Send Reply:~s~n",[Data]),
	Reply = io_lib:format("
	        <xml version=\"1.0\" encoding=\"UTF-8\">
		<rpc-reply message-id=\"1\">
		     ~s
		</rpc-reply>", [Data]),
	netconf_rpc:send_reply(Sock, Reply).
	
send_error(Sock, Data) -> ok.


%%% --------------------------------------------------------------

edit_match_element(Sock, [#xmlElement{name = target}=E|T]) 
   when record(E, xmlElement) ->
	%% io:format("CONFIG:~p~n",[E#xmlElement.content]),
	match_element_target(Sock, E#xmlElement.content),
	edit_match_element(Sock, T);

edit_match_element(Sock, [#xmlElement{name = running}=E|T]) 
   when record(E, xmlElement) ->
	%% io:format("SOURCE:~p~n",[E]),
	edit_match_element_config(Sock, E#xmlElement.content),
	edit_match_element(Sock, T);

edit_match_element(Sock, [#xmlElement{name = config}=E|T]) 
   when record(E, xmlElement) ->
	%% io:format("CONFIG:~p~n",[E]),
	edit_match_element_config(Sock, E#xmlElement.content),
	edit_match_element(Sock, T);

edit_match_element(Sock, [_H|T]) -> edit_match_element(Sock, T);
edit_match_element(_Sock, []) -> ok.

match_element_target(Sock, [#xmlElement{name = _Name}=E|_T]) 
   when record(E, xmlElement) ->
	%% io:format("Target Name :~p~n",[_Name]),
	match_element_target(Sock, E#xmlElement.content);

match_element_target(Sock, [_H|T]) -> match_element_target(Sock, T);
match_element_target(_Sock, []) -> ok.

edit_match_element_config(Sock, [#xmlElement{name = Name}=E|_T]) 
   when record(E, xmlElement) ->
	%% io:format("EDIT CONFIG Name:~p~n",[Name]),
	process_target(Sock, Name, E#xmlElement.content);
	%% match_element_config(E#xmlElement.content);

edit_match_element_config(Sock, [_H|T]) -> edit_match_element_config(Sock, T);
edit_match_element_config(_Sock, []) -> ok.

